package com.iretailer.report.util;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @Auther: xjw
 * @Date: 2019/1/18 16:38
 * @Description:
 */
public class TimeUtil {


    public static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static final DateTimeFormatter DEFAULT_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");


    /**
     * @Author xjw
     * @Date 2019/1/18 17:06
     * @Description :  获取 开始时间
     **/
    public static String getStartTime(String date) {
        return getDateTime(date, 0, 0, 0);
    }

    public static String getEndTime(String date) {
        return getDateTime(date, 23, 59, 59);
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 17:06
     * @Description :  获取 开始时间
     **/
    public static String getDateTime(String date, int hour, int minute, int second) {
        return localDateTime_format(LocalDateTime.of(parse_LocalDate(date), LocalTime.of(hour, minute, second)));
    }

    /**
     * @Author xjw
     * @Date 2019/3/11 13:08
     * @Description :  字符串 转为 LocalTime
     **/
    public static LocalTime parse_LocalTime(String time) {
        return parse_LocalTime(time, "HH:mm:ss");
    }

    public static LocalTime parse_LocalTime(String time, String formatterStr) {
        return LocalTime.parse(time, getDTF(formatterStr));
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 17:38
     * @Description :  字符串 转为 LocalDateTime
     **/
    public static LocalDateTime parse_LocalDateTime(String timeStr) {
        return parse_LocalDateTime(timeStr, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 17:38
     * @Description :  字符串 转为 LocalDateTime
     **/
    public static LocalDateTime parse_LocalDateTime(String timeStr, String formatterStr) {
        return LocalDateTime.parse(timeStr, getDTF(formatterStr));
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 16:48
     * @Description :  String date 转换为 LocalDate
     **/
    public static LocalDate parse_LocalDate(String dateStr) {
        return parse_LocalDate(dateStr, "yyyy-MM-dd");
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 16:48
     * @Description :  String date 转换为 LocalDate
     **/
    public static LocalDate parse_LocalDate(String dateStr, String formatterStr) {
        return LocalDate.parse(dateStr, getDTF(formatterStr));
    }

    /**
     * @Author xjw
     * @Date 2019/1/21 14:51
     * @Description :  LocalDate 转 String
     **/
    public static String localDate_format(LocalDate date) {
        return localDate_format(date, "yyyy-MM-dd");
    }

    public static String localDate_format(LocalDate date, String formatterStr) {
        return getDTF(formatterStr).format(date);
    }

    public static String localTime_format(LocalTime time) {
        return localTime_format(time, "HH:mm:ss");
    }

    public static String localTime_format(LocalTime time, String formatterStr) {
        return getDTF(formatterStr).format(time);
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 16:43
     * @Description :  LocalDateTime 格式化 yyyy-MM-dd HH:mm:ss
     **/
    public static String localDateTime_format(LocalDateTime dateTime) {
        return localDateTime_format(dateTime, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 16:43
     * @Description :  LocalDateTime 根据 formatterStr 格式化
     **/
    public static String localDateTime_format(LocalDateTime dateTime, String formatterStr) {
        return getDTF(formatterStr).format(dateTime);
    }


    /**
     * @Author xjw
     * @Date 2019/2/19 11:09
     * @Description :  String 转为 zoneDateTIme
     **/
    public static ZonedDateTime parse_ZonedDateTime(String dateStr) {
        return ZonedDateTime.parse(dateStr);
    }

    public static ZonedDateTime parse_ZonedDateTime(String dateStr, String formatterStr) {
        return ZonedDateTime.parse(dateStr, getDTF(formatterStr));
    }

    public static String zoneDateTime_format(ZonedDateTime dateTime) {
        return zoneDateTime_format(dateTime, "yyyy-MM-dd HH:mm:ss");
    }

    public static String zoneDateTime_format(ZonedDateTime dateTime, String formatterStr) {
        return getDTF(formatterStr).format(dateTime);
    }


    public static String getThisWeekMonday(LocalDate date) {
        int day = date.getDayOfWeek().getValue();
        if (day == 7) {
            return localDate_format(date);
        } else {
            return localDate_format(date.plusDays(-day));
        }
    }

    /**
     * @Author xjw
     * @Date 2019/1/21 16:33
     * @Description :  获取星期几 一 二 三 ...
     **/
    public static String getWeekOfLocalDate(LocalDate date) {
        int dayOfWeek = date.getDayOfWeek().getValue();
        return weekDays.get(dayOfWeek - 1);
    }

    public static String getWeekOfZonedDateTime(ZonedDateTime date) {
        int dayOfWeek = date.getDayOfWeek().getValue();
        return weekDays.get(dayOfWeek - 1);
    }

    public static String getISODateTime(LocalDateTime localDateTime, ZoneOffset offset) {
        OffsetDateTime date = OffsetDateTime.of(localDateTime, offset);
        return getDTF("yyyy-MM-dd'T'HH:mm:ssXXX").format(date);
    }

    /**
     * @Author xjw
     * @Date 2019/1/18 17:25
     * @Description :  获取星期几 带 周
     **/
//    public static String getWeekOfLocalDate02(LocalDate date) {
//        int dayOfWeek = date.getDayOfWeek().getValue();
//        return weekDays02.get(dayOfWeek - 1);
//    }

    /**
     * 获取时间的 long
     */
    public static long getLong(ZonedDateTime dateTime) {
        return dateTime.toInstant().toEpochMilli();
    }

    /**
     * @Author xjw
     * @Date 2019/1/21 16:13
     * @Description :  time 是否在 startTime 和 endTime 之间
     **/
    public static boolean compareTime(LocalDateTime startTime, LocalDateTime endTime, String time) {
        LocalDateTime dateTime = parse_LocalDateTime(time);
        if (!startTime.isAfter(dateTime) && !endTime.isBefore(dateTime)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @Author xjw
     * @Date 2019/1/21 16:14
     * @Description :  获取两个日期之间所有的日期
     **/
    public static List<String> getBetweenDays(String startDateStr, String endDateStr) {
        LocalDate startDate = parse_LocalDate(startDateStr, "yyyy-MM-dd");
        LocalDate endDate = parse_LocalDate(endDateStr, "yyyy-MM-dd");
        List<String> result = new ArrayList<>();

        if (startDate.isEqual(endDate)) {
            result.add(startDateStr);
            return result;
        } else {
            long differDays = endDate.toEpochDay() - startDate.toEpochDay();
            for (int i = 0; i <= differDays; i++) {
                LocalDate time = startDate.plusDays(i);
                result.add(localDate_format(time));
            }
            return result;
        }
    }

    /**
     * @Author xjw
     * @Date 2019/2/14 11:15
     * @Description :  得到数据的时间组
     **/
    public static List<String> getPeriodStr(String startDate_str, String endDate_str, String period, String timeFormat) {
        return getPeriodStr(startDate_str, endDate_str, true, LocalTime.of(0, 0, 0), LocalTime.of(23, 59, 59), period, timeFormat);
    }


    public static List<String> getPeriodStr(String startDate_str, String endDate_str, boolean includEnd, LocalTime startTime, LocalTime endTime, String period, String timeFormat) {

        LocalDate startDate = parse_LocalDate(startDate_str, "yyyy-MM-dd");
        LocalDate endDate = parse_LocalDate(endDate_str, "yyyy-MM-dd");
        return getPeriodStr(startDate, endDate, includEnd, startTime, endTime, period, timeFormat);
    }

    public static List<String> getPeriodStr(LocalDate startDate, LocalDate endDate, boolean includEnd, LocalTime startTime, LocalTime endTime, String period, String timeFormat) {
        Set<String> result = new TreeSet<>();
        switch (period) {
            case "All":
                result.add(period);
                break;
            case "Week":
                week(startDate, endDate, result);
                break;
            case "Weekday":
                result.addAll(weekDays);
                break;
            case "Hourly":
                hourly(startTime, endTime, result);
                break;
            case "WeekPeak":
                weekPeak(startDate, endDate, startTime, endTime, result);
                break;
            case "MonthPeak":
            case "YearPeak":
                peakTime(startDate, endDate, startTime, endTime, period, result);
                break;
            default:
                long differDays = endDate.toEpochDay() - startDate.toEpochDay();
                minuteOrNot(period, startDate, differDays, includEnd, startTime, endTime, timeFormat, result);
                break;
        }
        return new ArrayList<>(result);
    }

    private static void week(LocalDate startDate, LocalDate endDate, Set<String> result) {
        do {
            int day = startDate.getDayOfWeek().getValue();
            if (day == 7) {
                result.add(TimeUtil.localDate_format(startDate));
            } else {
                result.add(TimeUtil.localDate_format(startDate.plusDays(-day)));
            }
            startDate = startDate.plusDays(1);
        } while (!startDate.isAfter(endDate));

    }

    private static void weekPeak(LocalDate startDate, LocalDate endDate, LocalTime startTime, LocalTime endTime, Set<String> result) {
        Set<String> dates = new HashSet<>();
        do {
            int day = startDate.getDayOfWeek().getValue();
            if (day == 7) {
                dates.add(TimeUtil.localDate_format(startDate));
            } else {
                dates.add(TimeUtil.localDate_format(startDate.plusDays(-day)));
            }
            startDate = startDate.plusDays(1);
        } while (!startDate.isAfter(endDate));
        commonPeakTime(startTime, endTime, dates, result);
    }


    private static void peakTime(LocalDate startDate, LocalDate endDate, LocalTime startTime, LocalTime endTime, String period, Set<String> result) {

        Set<String> dates = new HashSet<>();
        do {
            LocalDate date = null;
            switch (period) {
                case "MonthPeak":
                    date = LocalDate.of(startDate.getYear(), startDate.getMonth(), 1);
                    break;
                case "YearPeak":
                    date = LocalDate.of(startDate.getYear(), 1, 1);
                    break;
            }
            dates.add(TimeUtil.localDate_format(date));
            startDate = startDate.plusDays(1);
        } while (!startDate.isAfter(endDate));
        commonPeakTime(startTime, endTime, dates, result);
    }

    private static void commonPeakTime(LocalTime startTime, LocalTime endTime, Set<String> dates, Set<String> result) {
        int allPeriod = (endTime.getHour() - startTime.getHour()) * 60 + (endTime.getMinute() - startTime.getMinute());
        int p = 60;
        int allM = allPeriod / p;
        for (String date : dates) {
            LocalDate d = parse_LocalDate(date, "yyyy-MM-dd");
            int m = 0;
            do {
                LocalDateTime dateTime = LocalDateTime.of(d, startTime.plusMinutes((p * m)));
                result.add(localDateTime_format(dateTime, "yyyy-MM-dd HH:mm"));
            } while (m++ < allM);
        }
    }


    private static void minuteOrNot(String period, LocalDate startDate, long differDays, boolean includEnd, LocalTime startTime, LocalTime endTime, String timeFormat, Set<String> result) {
        int i = 0;
        int allM = 0;
        int p = 0;
        if (period.contains("m")) { //分钟
            p = Integer.parseInt(period.replace("m", ""));
            int allPeriod = (endTime.getHour() - startTime.getHour()) * 60 + (endTime.getMinute() - startTime.getMinute());
            allM = allPeriod / p;
            if (allM <= 0) {
                allM = 1;
            }
        }

        do {
            switch (allM) {
                case 0:
                    LocalDateTime time = LocalDateTime.of(startDate.plusDays(i), startTime);
                    result.add(localDateTime_format(time, timeFormat));
                    break;
                default:
                    int m = 0;
                    // 如果不包含结尾 则 --
                    if (!includEnd) {
                        allM--;
                    }
                    do {
                        LocalDateTime dateTime = LocalDateTime.of(startDate.plusDays(i), startTime.plusMinutes((p * m)));
                        result.add(localDateTime_format(dateTime, timeFormat));
                    } while (m++ < allM);
                    break;
            }

        } while ((i++) < differDays);
    }

    public static void hourly(LocalTime startTime, LocalTime endTime, Set<String> result) {
        int allPeriod = (endTime.getHour() - startTime.getHour()) * 60 + (endTime.getMinute() - startTime.getMinute());
        int p = 60;
        int allM = allPeriod / p;
        int m = 0;
        do {
            LocalTime time = startTime.plusMinutes((p * m));
            result.add(localTime_format(time, "HH"));
        } while (m++ < allM);
    }

    public static final List<String> weekDays = Arrays.asList("Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon");

    public static int getWeekDaysIndex(String weekday) {
        switch (weekday) {
            case "Mon":
                return 1;
            case "Tue":
                return 2;
            case "Wed":
                return 3;
            case "Thu":
                return 4;
            case "Fri":
                return 5;
            case "Sat":
                return 6;
            case "Sun":
                return 7;
            default:
                return -1;
        }
    }

    //    public static final List<String> weekDays02 = Arrays.asList("周二", "周三", "周四", "周五", "周六", "周日", "周一");
    //锁对象
    private static final Object lockObj = new Object();
    private static volatile Map<String, ThreadLocal<DateTimeFormatter>> sdfMap = new HashMap<>();

    private static DateTimeFormatter getDTF(final String pattern) {
        ThreadLocal<DateTimeFormatter> tl = sdfMap.get(pattern);
        //此处双重判断和同步是为了防止 sdfMap这个单例多次put重复的 sdf
        if (tl == null) {
            synchronized (lockObj) {
                tl = sdfMap.get(pattern);
                if (tl == null) {
                    tl = new ThreadLocal<DateTimeFormatter>() {
                        @Override
                        protected DateTimeFormatter initialValue() {
                            return DateTimeFormatter.ofPattern(pattern);
                        }
                    };
                    sdfMap.put(pattern, tl);
                }
            }
        }
        return tl.get();
    }

}
